home *** CD-ROM | disk | FTP | other *** search
- /*
- File: OTTraceRouteSample.c
-
- Contains: A trivial traceroute implementation.
-
- Written by: Quinn "The Eskimo!"
-
- Copyright: © 1996 by Apple Computer, Inc., all rights reserved.
-
- Change History (most recent first):
-
- You may incorporate this sample code into your applications without
- restriction, though the sample code has been provided "AS IS" and the
- responsibility for its operation is 100% yours. However, what you are
- not permitted to do is to redistribute the source as "DSC Sample Code"
- after having made changes. If you're going to re-distribute the source,
- we require that you make it clear in the source that the code was
- descended from Apple Sample Code, but that you've made changes.
- */
-
- #include <stdio.h>
- #include <OpenTransport.h>
- #include <OpenTptInternet.h>
-
- /////////////////////////////////////////////////////////////////////
-
- static OSStatus CreateAndConfigUDP(EndpointRef *ep)
- {
- OSStatus err;
-
- *ep = OTOpenEndpoint(OTCreateConfiguration(kUDPName), 0, nil, &err);
-
- if (err == noErr) {
- err = OTBind(*ep, nil, nil);
-
- // no others options to negotiate at this stage
- }
-
- return (err);
- }
-
- /////////////////////////////////////////////////////////////////////
-
- static OSStatus CreateAndConfigICMP(EndpointRef *ep)
- {
- OSStatus err;
-
- *ep = OTOpenEndpoint(OTCreateConfiguration(kRawIPName), 0, nil, &err);
-
- if (err == noErr) {
- err = OTBind(*ep, nil, nil);
-
- // no others options to negotiate at this stage
-
- // You might think we need to negotiate the XTI_GENERIC/XTI_PROTOTYPE
- // option to request ICMP packets (ie protocol 2). This is not
- // necessary because rawip endpoints default to that protocol.
- }
-
- return (err);
- }
-
- /////////////////////////////////////////////////////////////////////
-
- static OSStatus DoNegotiateIP_TTLOption(EndpointRef ep, long ttl)
- {
- // According to the XTI spec, IP_TTL is an INET_IP level option that
- // determines the TTL of an IP packet. The value of this option is
- // a UInt8. This routine simply negotiates that option on the ep
- // endpoint.
- OSStatus err;
- TOption* opt; // points to buf, makes it easier to access
- TOptMgmt req;
- TOptMgmt ret;
- UInt8 buf[kOTFourByteOptionSize]; // define buffer for options, although we only
- // use a "1 byte option", we define a "4 byte option"
- // buffer to hold the returning options
-
- // Point opt to the start of buf. This allows us to set the items in buf easily.
- opt = (TOption*)buf;
-
- // Setup the fields of the options buffer...
-
- opt->level = INET_IP;
- opt->name = IP_TTL;
- opt->len = kOTOneByteOptionSize; // Note that kOTOneByteOptionSize != 1, it also
- opt->status = 0; // includes the size of the option header.
- *(UInt8*)opt->value = ttl;
-
- // Set up the req structure to denote the options we're requesting...
-
- req.opt.buf = buf;
- req.opt.len = kOTOneByteOptionSize;
- req.flags = T_NEGOTIATE;
-
- // Set up the ret structure to hold the options we got...
-
- ret.opt.buf = buf;
- ret.opt.maxlen = kOTFourByteOptionSize;
-
- err = OTOptionManagement(ep, &req, &ret);
-
- // If no error then return the option status value...
-
- if (err == kOTNoError) {
- if (opt->status != T_SUCCESS)
- err = opt->status;
- else
- err = kOTNoError;
- }
-
- return (err);
- }
-
- /////////////////////////////////////////////////////////////////////
-
- // this is the data we send in our UDP packets...
-
- static unsigned char udp_data[8] = {0, 1, 2, 3, 4, 5, 6, 7};
-
- /////////////////////////////////////////////////////////////////////
-
- static OSStatus SendUDPWithTTL(EndpointRef ep, InetHost dest, long ttl)
- {
- OSStatus err;
- InetAddress dest_addr;
- TUnitData udata;
- OTResult look;
-
- err = DoNegotiateIP_TTLOption(ep, ttl);
-
- if (err == noErr) {
-
- OTInitInetAddress(&dest_addr, 33434, dest);
-
- // 33434 is the default port for unix traceroute.
- // It was chosen because it's unlikely that anyone will be listening on this
- // port. Hence any packets that make it through will generate an ICMP
- // port unreachable error.
-
- udata.addr.len = sizeof(dest_addr);
- udata.addr.buf = (unsigned char *) &dest_addr;
-
- udata.opt.len = 0;
- udata.opt.buf = nil;
-
- udata.udata.len = sizeof(udp_data);
- udata.udata.buf = &udp_data[0];
-
- // The act of sending is a little more complicated than it should be.
- // Basically the ICMP errors that come back from all these bogus (short TTL)
- // packets that I send, end up as datagram errors on the sending endpoint.
- // If you attempt to send with a T_UDERR sitting on the endpoint, you get
- // a kOTLookErr.
- //
- // I addresses this by junking the error and looping when I get a T_UDERR.
-
- do {
- err = OTSndUData(ep, &udata);
- if (err == kOTLookErr) {
- look = OTLook(ep);
- if (look == T_UDERR) {
- printf("•Junking T_UDERR.\n");
- fflush(stdout);
- (void) OTRcvUDErr(ep, nil); // clear the error condition without receiving the error info
- err = 666; // and attempt to send again
- // Yeah, yeah, I know that using error codes to control program flow is bad
- // style. Hey, I was in hurry!
- }
- }
- } while (err == 666);
- }
-
- return (err);
- }
-
- /////////////////////////////////////////////////////////////////////
-
- // we use this buffer to hold incoming ICMP packets
-
- static UInt8 icmp_data[5000];
-
- /////////////////////////////////////////////////////////////////////
-
- static OSStatus WaitAndPrintICMPs(EndpointRef ep, Boolean *done)
- {
- TUnitData udata;
- long start_time;
- OSStatus err;
- InetAddress src_addr;
-
- start_time = TickCount();
-
- // Wait for 3 seconds and print out any ICMP packets we get back.
-
- do {
-
- // Set up the received...
- udata.addr.buf = (UInt8*) &src_addr;
- udata.addr.maxlen = sizeof(struct InetAddress);
- udata.opt.buf = nil;
- udata.opt.maxlen = 0;
- udata.udata.buf = icmp_data;
- udata.udata.maxlen = sizeof(icmp_data);
-
- // Look for a packet...
-
- err = OTRcvUData(ep, &udata, nil);
- if (err == noErr) {
- // Print out salient information from the packet...
- printf("•••Got packet!•••\n");
-
- printf("ICMP from = %d.%d.%d.%d\n", icmp_data[12], icmp_data[13], icmp_data[14], icmp_data[15]);
- printf("ICMP type = %d\n", icmp_data[20]);
- printf("ICMP code = %d\n", icmp_data[21]);
-
- // Stop if the traceroute is at an end. Note that this code assumes that
- // the ICMP header will start 20 bytes into the packet. This is correct
- // for 99% of IP packets, but not correct in general. If the IP packet
- // has IP level options, they will be inserted between the 20 byte IP
- // header and the payload, thereby stuffing up this calculation. I was
- // slack and ignored this issue. You should not!
-
- if (icmp_data[20] == 3 && icmp_data[20] == 3) {
- // type 3 = destination unreachable
- // code 3 = port unreachable
- // These two imply that we're trying to deliver the packet on the destination
- // host, and it couldn't be delivered because the port is wrong. The fact
- // that we're hitting the destination host means we can stop the trace.
- *done = true;
- }
-
- fflush(stdout);
- } else if (err == kOTNoDataErr) {
- err = noErr;
- }
- } while (err == noErr && TickCount() < start_time + 3 * 60 && !*done);
-
- return (err);
- }
-
- /////////////////////////////////////////////////////////////////////
-
- static OSStatus DoTraceRoute(InetHost dest)
- {
- OSStatus err;
- EndpointRef udp_ep = nil;
- EndpointRef icmp_ep = nil;
- long ttl;
- Boolean done;
-
- // Create the endpoints and negotiate the options...
- err = CreateAndConfigUDP(&udp_ep);
- if (err == noErr) {
- err = CreateAndConfigICMP(&icmp_ep);
- }
-
- // Do the main traceroute loop...
-
- ttl = 1;
- done = false;
- do {
- printf("\nSending with TTL = %d.\n", ttl);
- err = SendUDPWithTTL(udp_ep, dest, ttl);
- if (err == noErr) {
- err = WaitAndPrintICMPs(icmp_ep, &done);
- }
- if (err == noErr) {
- ttl += 1;
- }
- } while (err == noErr && ttl < 30 && !done);
-
- if (done) {
- printf("Traceroute completed successfully!\n");
- }
-
- // clean up
- if (udp_ep != nil) {
- (void) OTCloseProvider(udp_ep);
- }
- if (icmp_ep != nil) {
- (void) OTCloseProvider(icmp_ep);
- }
- return err;
- }
-
- /////////////////////////////////////////////////////////////////////
-
- void main(void) {
- OSStatus err;
-
- printf("Hello Cruel World!\n");
-
- err = InitOpenTransport();
- if (err == noErr) {
-
- err = DoTraceRoute(0x822B0202); // apple.com
-
- CloseOpenTransport();
- }
-
- if (err == noErr) {
- printf("Success!\n");
- } else {
- printf("Failure! Error = %d.\n", err);
- }
- printf("Done. Press command-Q to Quit.\n");
- }